using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Management.Automation;
using System.Collections;

namespace YaTools.Yaml
{
    [Cmdlet(VerbsData.Export, "YAML", SupportsShouldProcess = true)]
    public class ExportYamlCommand : YamlWriterCommand
    {
        [Parameter(ValueFromPipeline = true, Mandatory = true,
            ValueFromPipelineByPropertyName = true)]
        public PSObject InputObject { get; set; }

        [Parameter(Mandatory=true, Position=0)]
        [Alias("PSPath")]
        public string Path { get; set; }

        [Parameter]
        public SwitchParameter Append { get; set; }

        [Parameter]
        public SwitchParameter Force { get; set; }

        [Parameter]
        public SwitchParameter NoClobber { get; set; }

        [Parameter]
        public SwitchParameter PassThru { get; set; }

        ArrayList _accumulator;

        private string ResolveFilePath()
        {
            string result = null;
            ProviderInfo provider;
            try
            {
                var paths = GetResolvedProviderPathFromPSPath(Path, out provider);
                if (paths.Count > 1) ReportMultipleFilesNotSupported();
                if (paths.Count < 1) ReportWildcardingFailure(Path);
                result = paths[0];
            }
            catch (ItemNotFoundException)
            {
                PSDriveInfo drive;
                result = SessionState.Path.GetUnresolvedProviderPathFromPSPath(
                    Path, out provider, out drive);
            }
            if (provider.Name != "FileSystem") ReportWrongProviderType(provider.Name);
            return result;
        }
        
        protected override void BeginProcessing()
        {
            if (base.ShouldProcess(Path, "Write YAML to"))
            {
                _accumulator = new ArrayList();
            }
        }

        protected override void ProcessRecord()
        {
            try
            {
                if (_accumulator != null) _accumulator.Add(InputObject);
                if (PassThru) WriteObject( InputObject );
            }
            catch (Exception)
            {
                throw;
            }
        }

        protected override void EndProcessing()
        {
            if (_accumulator != null)
            {
                string filePath = ResolveFilePath();

                FileMode mode = Append ? FileMode.Append :
                    (NoClobber ? FileMode.CreateNew : FileMode.Create);
                if (Force && (Append || !NoClobber) && File.Exists(filePath))
                {
                    FileInfo info = new FileInfo(filePath);
                    if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                    {
                        info.Attributes &= ~FileAttributes.ReadOnly;
                    }
                }
                FileShare share = (Force ? FileShare.ReadWrite : FileShare.Read) | FileShare.Delete;
                var stream = new FileStream(filePath, mode, FileAccess.Write, share);
                using (var writer = new StreamWriter(stream, Encoding.UTF8))
                {
                    object value = _accumulator.Count == 1 ? _accumulator[0] : _accumulator;

                    var dumper = new Dump(Dump.Presenter.Yaml.Flow.Instance);
                    dumper.Serialize(writer, value);
                }
                base.EndProcessing();
            }
        }

        void ReportMultipleFilesNotSupported()
        {
            string message = "Multiple output files not supported";
            ErrorRecord errorRecord = new ErrorRecord( 
                new InvalidOperationException(), 
                "ReadWriteMultipleFilesNotSupported", 
                ErrorCategory.InvalidArgument, null);
            errorRecord.ErrorDetails = new ErrorDetails(message);
            ThrowTerminatingError(errorRecord);
        }

        void ReportWildcardingFailure(string filePath)
        {
            string message = "Wildcard didn't match any items";
            ErrorRecord errorRecord = new ErrorRecord(
                new FileNotFoundException(),
                "FileOpenFailure",
                ErrorCategory.OpenError, filePath);
            errorRecord.ErrorDetails = new ErrorDetails(message);
            ThrowTerminatingError(errorRecord);
        }

        void ReportWrongProviderType(string provider)
        {
            string message = "Non-file provider not supported";
            ErrorRecord errorRecord = new ErrorRecord(
                new InvalidOperationException(), "ReadWriteFileNotFileSystemProvider",
                ErrorCategory.InvalidArgument, null);
            errorRecord.ErrorDetails = new ErrorDetails(message);
            ThrowTerminatingError(errorRecord);
        }

    }
}
